/**
 * @author Nicolas Zimerman (zimerman@usi.ch)
 * @author Isabella Nake (isabella.nake@usi.ch)
 * @author Nicolas Lusa (lusan@usi.ch)
 * @author Nicolas Latorre (nicolas.latorre@usi.ch)
 */
package ch.usi.inf.pf2.circuit;

import java.util.ArrayList;
import java.util.Iterator;

import ch.usi.inf.pf2.gui.CircuitListener;
import ch.usi.inf.pf2.gui.ComponentListener;

public final class Circuit implements Iterable<Component>{
	
	private ArrayList<Component> shapes;
    private ArrayList<CircuitListener> listeners;
    private ArrayList<ComponentListener> compListeners;
	
	public Circuit() {
		shapes = new ArrayList<Component>();
        listeners = new ArrayList<CircuitListener>();
        compListeners = new ArrayList<ComponentListener>();
	}
	
	public Circuit(final ArrayList<Component> circuit) {
		shapes = circuit;
        listeners = new ArrayList<CircuitListener>();
        compListeners = new ArrayList<ComponentListener>();
	}
	
	//GUI PART
	
	public ArrayList<Component> getShapes() {
		return shapes;
	}
	
	public int getNumberOfShapes() {
        return shapes.size();
    }
    
    public int getNumberOfNotGates() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof NotGate) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public int getNumberOfAndGates() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof AndGate) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }

    public int getNumberOfOrGates() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof OrGate) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public int getNumberOfNorGates() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof NorGate) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public int getNumberOfXorGates() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof XorGate) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public int getNumberOfNandGates() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof NandGate) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public int getNumberOfInputs() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof Input) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public int getNumberOfOutputs() {
    	int i = 0;
    	for(Component shape: shapes){
    		if(shape instanceof Output) {
    			i++;
    		}
    		else{}
    	}
    	return i;
    }
    
    public void shapeChanged() {
    	fireComponentChanged();
    }
    
    public void add(final Component shape) {
        shapes.add(shape);
        fireCircuitChanged();
    }
    
    public void remove(final Component shape) {
        shapes.remove(shape);
        Wire[] wires = shape.getWires();
        for(Wire wire: wires) {
        	if(wire != null) {
        		wire.removePins();
        	}
        	shapes.remove(wire);
        }
        fireCircuitChanged();
    }
    
    public Component findShapeAt(final int x, final int y) {
        //System.out.println("findShapeAt("+x+","+y+")");
        for (final Component shape : shapes) {
            //System.out.println("  shape: "+shape);
            if (shape.contains(x, y)) {
                //System.out.println("  found!");
                return shape;
            }
        }
        return null;
    }
    
    public ArrayList<Input> getInputs() {
    	ArrayList<Input> inputs = new ArrayList<Input>();
    	for(Component shape: shapes) {
    		if(shape instanceof Input) {
    			inputs.add((Input) shape);
    		}
    	}
    	return inputs;
    }
	    
	    
	public int getFurtherstComponentPositionX() {
		int x = 0;
		for (final Component shape : shapes) {
			if (shape.getOriginX() >= x) {
				x = shape.getOriginX();
			}
		}
		return x;
	}

	public int getFurtherstComponentPositionY() {
		int y = 0;
		for (final Component shape : shapes) {
			if (shape.getOriginY() >= y) {
				y = shape.getOriginY();
			}
		}
		return y;
	}
    
    public Iterator<Component> iterator() {
        return new Iterator<Component>() {
            private int position = 0;
            public boolean hasNext() {
                return position < shapes.size();
            }
            public Component next() {
                return shapes.get(position++);
            }
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
    
    
    //--- listener management
    public void addCircuitListener(final CircuitListener li) {
        listeners.add(li);
    }
    
    private void fireCircuitChanged() {
        for (final CircuitListener li : listeners) {
            li.circuitChanged();
        }
    }
    
    public void addComponentListener(final ComponentListener cli) {
        compListeners.add(cli);
    }
    
    private void fireComponentChanged() {
        for (final ComponentListener cli : compListeners) {
            cli.componentChanged();
        }
    }
    
    
    
}
